using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;

namespace Bread.Utility;

public static class StringHelper
{
    public static T? Value<T>(this string input) where T : IParsable<T>
    {
        if (T.TryParse(input, null, out T? value)) {
            return value;
        }
        return default(T);
    }

    public static object? Value(this string input, Type type)
    {
        try {
            return TypeDescriptor.GetConverter(type).ConvertFromString(input);
        }
        catch {
            return null;
        }
    }

    /// <summary>
    /// 将路径进行URLEncode
    /// 此方法仅用于输出FCPXML
    /// </summary>
    /// <param name="path"></param>
    public static string EncodeFilePath(this string path)
    {
        string url = HttpUtility.UrlEncode(path);
        url = url.Replace("%5c", "/"); //替换回“/”
        return url.Replace("+", "%20");//过滤掉“+”
    }

    public static string Wrap(this string input, int maxLength, string demiter = "...")
    {
        if (maxLength <= demiter.Length + 1) return demiter;

        if (input.Length < maxLength) {
            return input;
        }

        int trimLength = (maxLength - demiter.Length) / 2;
        return input.Substring(0, trimLength) + demiter + input.Substring(input.Length - trimLength);
    }


    /// <summary>
    /// return true if str is null or empty
    /// </summary>
    public static bool IsEmpty([NotNullWhen(false)] this string? str)
    {
        if (str == null) return true;
        if (str!.Length == 0) return true;
        return false;
    }

    /// <summary>
    /// return false if str is null or empty
    /// </summary>
    public static bool IsNotEmpty([NotNullWhen(true)] this string? str)
    {
        if (str == null) return false;
        if (str.Length == 0) return false;
        return true;
    }

    /// <summary>
    /// return true if str is null or whitesapce
    /// </summary>
    public static bool IsBlank([NotNullWhen(false)] this string? str)
    {
        if (str == null) return true;
        if (str.Length == 0) return true;
        foreach (var ch in str) {
            if (ch != ' ') {
                return false;
            }
        }
        return true;
    }

    /// <summary>
    /// return true if str is not null and not whitesapce
    /// </summary>
    public static bool IsNotBlank([NotNullWhen(true)] this string? str)
    {
        if (str == null) return false;
        if (str.Length == 0) return false;
        foreach (var ch in str) {
            if (ch != ' ') {
                return true;
            }
        }
        return false;
    }

    /// <summary>
    /// if str is null or empty return value otherwise return str itself.
    /// </summary>
    public static string IfEmpty(this string? str, string value)
    {
        if (str is null) return value;
        if (str.Length == 0) return value;
        return str;
    }

    /// <summary>
    /// if str is null or white sapce return value otherwise return str itself.
    /// </summary>
    public static string IfBlank(this string? str, string value)
    {
        if (str is null) return value;
        if (str.Length == 0) return value;
        foreach (var ch in str) {
            if (ch != ' ') {
                return str;
            }
        }
        return value;
    }

    public static string Concat(this string separator, List<string>? list)
    {
        if (list is null) return string.Empty;

        var builder = new StringBuilder();
        for (var i = 0; i < list.Count; i++) {
            var str = list[i];
            if (string.IsNullOrWhiteSpace(str)) continue;
            builder.Append(str);
            if (i == list.Count - 1) break;
            builder.Append(separator);
        }
        var result = builder.ToString();
        if (result.EndsWith(separator)) {
            return result.Substring(0, result.Length - separator.Length);
        }
        return result;
    }

    public static string Concat(string separator, params string[]? strs)
    {
        if (strs is null) return string.Empty;

        var builder = new StringBuilder();
        for (var i = 0; i < strs.Length; i++) {
            var str = strs[i];
            if (string.IsNullOrWhiteSpace(str)) continue;
            builder.Append(str);
            if (i == strs.Length - 1) break;
            builder.Append(separator);
        }
        var result = builder.ToString();
        if (result.EndsWith(separator)) {
            return result.Substring(0, result.Length - separator.Length);
        }
        return result;
    }

    /// <summary> 
    /// 检测含有中文字符串的实际长度 
    /// </summary> 
    /// <param name="str">字符串</param> 
    public static int GetChineseLength(string str)
    {
        System.Text.ASCIIEncoding n = new System.Text.ASCIIEncoding();
        byte[] b = n.GetBytes(str);
        int l = 0; // l 为字符串之实际长度 
        for (int i = 0; i <= b.Length - 1; i++) {
            if (b[i] == 63) //判断是否为汉字或全脚符号 
            {
                l++;
            }
            l++;
        }
        return l;

    }

    /// <summary>
    /// 提取字符串中的电话号
    /// * 兼容11位手机号、3位或4位区号+8位电话号、400电话的两种写法(400-ddd-dddd、400ddddddd)
    /// </summary>
    /// <param name="input">输入的字符串</param>
    /// <returns></returns>
    public static List<string> GetTelephoneList(string input)
    {
        //集合存放提取出来的电话号码
        List<string> list = new List<string>();

        /*
         * 正则表达式提取
         * 分为五种格式，能兼容11位手机号、3位或4位区号-7位或8位电话号、400电话的两种写法(400-ddd-dddd、400ddddddd)
         */
        Regex regex = new Regex(@"(1[3|4|5|6|7|8|9]\d{9})|(0\d{2,3}-\d{7,8})|(400-\d{3}-\d{4})|(400\d{7})");

        //Match集合，匹配成功的字符串集合
        MatchCollection collection = regex.Matches(input);

        //遍历Match集合，取出值
        string telephone;
        foreach (Match item in collection) {
            foreach (Group group in item.Groups) {
                telephone = group.Value.Trim();
                //偶尔会出现重复提取，所以加了去重判断
                if (!string.IsNullOrEmpty(telephone) && !list.Contains(telephone)) {
                    list.Add(telephone);
                }
            }
        }

        return list;
    }
}
